
#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    backupManager -- the backup class for AnyBackup, it contains the drive objects, meta data, and all the backup/restore logic
    """


from file import file
from drive import drive
import ConfigParser
import DriveUtil
import sys
import re
import shutil
import os
import hashlib
import logging
from time import sleep
from mixins import messagingMixin, pathMixin
from operator import attrgetter

messagingQueue = None
errorQueue = None
errorAnswerQueue = None

logger = logging.getLogger('AnyBackup')

def file_cmp(a,b):
    return cmp(a.getRoot().lower(),b.getRoot().lower()) or cmp(a.getPath().lower(),b.getPath().lower()) or cmp(a.getName().lower(),b.getName().lower())

class backupManager(messagingMixin, pathMixin):
    """ the backup class for AnyBackup, it contains the drive objects, meta data and all the backup/restore logic """
    BACKUP=2
    CONTENT=3
    ALL=4
    DUPLICATE=8
    UNNAMED=7
    INVALIDCONFIG=6
    MISSING=9
    BALANCED='balanced'
    STICKY='sticky'
    version = '0.9.4'

    def __init__(self,setName):
        self._setName = setName
        self._backupDrives = []
        self._contentDrives = []
        self._skipList = []
        self._validExt = []
        self.readConfigs()
        
    def getSetName(self):
        try:
            return self._setName
        except AttributeError:
            self._setName = 'AnyBackup'
            return self._setName
    
    def setName(self,setName):
        self._setName = setName
        
    def setErrorQueue(self,queue):
        global errorQueue
        errorQueue = queue
        
    def setErrorAnswerQueue(self,queue):
        global errorAnswerQueue
        errorAnswerQueue = queue
    
    def sendError(self,error):
        if errorQueue:
            errorQueue.put(error,1)
        else:            
            query = raw_input('{0} not connected, connect and proceed? (y/n):'.format(error))
            if query == 'y':
                self.answer = True
            else:
                self.answer = False
        
    def getErrorAnswer(self):
        if not errorAnswerQueue:
            try:
                return self.answer
            except AttributeError:
                return False
        else:
            while errorAnswerQueue.empty():
                sleep(1)
            return errorAnswerQueue.get_nowait()
        
    def setMessagingQueue(self,queue):
        global messagingQueue
        messagingQueue = queue
        for drive in self._backupDrives + self._contentDrives:
            drive.setMessagingQueue(queue)
        
    def toggleDrive(self,driveRef):
        if driveRef.isBackup():
            self._backupDrives.remove(driveRef)
            self._contentDrives.append(driveRef)
            driveRef.setIsBackup(False)
        else:
            self._contentDrives.remove(driveRef)
            self._backupDrives.append(driveRef)
            driveRef.setIsBackup(True)
        return True

    def readConfigs(self):
        config = ConfigParser.RawConfigParser()
        try:
            config.read('AnyBackup.cfg')
            backupMode = config.get('Backup','drive-selection')
            if backupMode == self.BALANCED or backupMode == self.STICKY:
                self.backupMode = backupMode
            else:
                self.backupMode = self.BALANCED
                return self.INVALIDCONFIG
        except:
            self.backupMode = self.BALANCED
            return self.INVALIDCONFIG
        return True

    def getBackupMode(self):
        return self.backupMode

    def sortFiles(self,files):
        files.sort(file_cmp)
        return files

    def getDrive(self,fileRef):
        for driveRef in self._backupDrives + self._contentDrives:
            if fileRef.getVolName() == driveRef.getName() and fileRef.getSerial() == driveRef.getSerial():
                return driveRef
        return None
    
    def remoteIndexDrive(self,driveRef):
        for driveObj in self._contentDrives:
            if driveRef.getName() == driveObj.getName() and driveRef.getSerial() == driveObj.getSerial():
                logger.info('Remote indexing {0}-{1}'.format(driveObj.getName(),driveObj.getSerial()))
                return driveObj.remoteIndex()

    def refreshDrive(self,driveRef):
        for driveObj in self._backupDrives + self._contentDrives:
            if driveRef.getName() == driveObj.getName() and driveRef.getSerial() == driveObj.getSerial():
                logger.info('Refreshing {0}-{1}'.format(driveObj.getName(),driveObj.getSerial()))
                return driveObj.indexDrive()

    def refreshDrives(self,type):
        if type is None:
            type = backupManager.ALL
        drives = []
        if type == backupManager.ALL or type == backupManager.BACKUP:
            drives += self._backupDrives
        if type == backupManager.ALL or type == backupManager.CONTENT:
            drives += self._contentDrives
        for driveRef in drives:
            logger.info('Refreshing {0}-{1}'.format(driveRef.getName(),driveRef.getSerial()))
            driveRef.indexDrive()

    def getSkipList(self):
        return self._skipList

    def addSkip(self, skip):
        if skip:
            logger.info('Add to skip list: {0}'.format(skip))
            self._skipList.append(skip)
            for driveRef in self._contentDrives + self._backupDrives:
                driveRef.setSkipList(self.getSkipList())

    def setSkipList(self,skip):
        if skip:
            self._skipList = skip
            for driveRef in self._contentDrives + self._backupDrives:
                driveRef.setSkipList(skip)

    def getValidExt(self):
        return self._validExt

    def addValidExt(self,ext):
        if ext:
            logger.info('Add to extension list: {0}'.format(skip))
            self._validExt.append(ext)
            for driveRef in self._contentDrives + self._backupDrives:
                driveRef.setValidExt(self.getValidExt())

    def setValidExt(self,ext):
        if ext:
            self._validExt = ext
            for driveRef in self._contentDrives + self._backupDrives:
                driveRef.setValidExt(ext)

    def addBackupDrive(self,letter,index=True):
        return self.addDrive(letter,backupManager.BACKUP,index)

    def addContentDrive(self,letter,paths,index=True):
        return self.addDrive(letter,backupManager.CONTENT,index,paths)

    def removeDrive(self,driveIn):
        if driveIn is None:
            logger.error('Undefined drive reference, check your selection')
            return False
        for driveRef in self._backupDrives[:] + self._contentDrives[:]:
            if driveRef.getName() == driveIn.getName() and driveRef.getSerial() == driveIn.getSerial():
                try:
                    self._backupDrives.remove(driveRef)
                except ValueError:
                    self._contentDrives.remove(driveRef)
                return True
        return False

    def setBackupDrives(self, backupDrives):
        if backupDrives:
            self._backupDrives = backupDrives

    def setContentDrives(self, contentDrives):
        if contentDrives:
            self._contentDrives = contentDrives

    def getBackupDrives(self):
        return self._backupDrives

    def getContentDrives(self):
        return self._contentDrives

    def searchFiles(self,regex):
        results = []
        for driveRef in self._contentDrives + self._backupDrives:
            driveResults = driveRef.search(regex)
            if driveResults:
                results += driveResults
        return results

    def uniqueCheck(self,name,serial):
        for driveRef in self._contentDrives + self._backupDrives:
            if driveRef.getName() == name and driveRef.getSerial() == serial:
                logger.error("You've already added " + name + " - " + serial + "!")
                return False
            elif driveRef.getName() == name:
                logger.warn("WARNING: " + name + " -- You've got two drives with the same Volume Name -- this is a bad idea! (Repent!)")
        return True

    def addDrive(self,letter,type,index=True,paths=None):
        name = DriveUtil.getName(letter)
        serial = DriveUtil.getSerial(letter)
        totalSpace = DriveUtil.getTotalSpace(letter)
        freeSpace = DriveUtil.getFreeSpace(letter)
        logger.info('Adding drive {0}:'.format(letter))
        if not DriveUtil.exists(letter):
            logger.error('Drive {0}:\\ does not exist!'.format(letter))
            return self.MISSING
        if not name:
            logger.error('Drive {0}:\\ must be named before adding!'.format(letter))
            return self.UNNAMED
        if name and serial and totalSpace and freeSpace:
            isBackup = False;
            if type == backupManager.BACKUP:
                isBackup = True
            if paths:
                newDrive = drive(name,serial,totalSpace,freeSpace,isBackup,self.getSetName(),paths=[])
                newDrive.setMessagingQueue(messagingQueue)
                for path in paths:
                    newDrive.addDir(path)
            else:
                newDrive = drive(name,serial,totalSpace,freeSpace,isBackup,self.getSetName())
            newDrive.setValidExt(self._validExt)
            newDrive.setSkipList(self._skipList)
            if not self.uniqueCheck(name,serial):
                logger.error('Drive {0}:\\ has already been added!'.format(letter))
                return backupManager.DUPLICATE
            if index:
                newDrive.indexDrive()
                newDrive.setMessagingQueue(messagingQueue)
            if type == backupManager.BACKUP:
                self._backupDrives.append(newDrive)
            elif type == backupManager.CONTENT:
                self._contentDrives.append(newDrive)
            logger.info('Drive {0}:\\ added'.format(letter))
            return True
        return False

    def hashify(self,fileIn,skiplist,hash,):
        for fileRef in fileIn.getFileList():
            key = self.getKey(fileRef)
            if key in hash:
                hash[key],oldFile=self.chooseFileCopy(hash[key],fileRef)
            else:
                hash[key]=fileRef
        return hash
    
    def hashifyDuplicates(self,fileIn,skiplist,hash=None):
        dupes = []
        if not hash:
            hash = {}
        for fileRef in fileIn.getFileList():
            key = self.getKey(fileRef)
            if key in hash:
                hash[key],dupe=self.chooseFileCopy(hash[key],fileRef)
                dupes.append(dupe)
            else:
                hash[key]=fileRef
        return hash,dupes

    def chooseFileCopy(self,file1,file2):
        if self.getBackupDrive(file1).isLocked():
            return file2,file1
        else:
            return file1,file2
    
    def escapeString(self,str):
        str = re.sub("\?","",str)
        str = re.sub("\+","",str)
        str = re.sub("\[","",str)
        str = re.sub("\-","",str)
        str = re.sub("\&","",str)
        str = re.sub("\]","",str)
        str = re.sub("\(","",str)
        str = re.sub("\)","",str)
        str = re.sub("\{","",str)
        str = re.sub("\}","",str)
        while re.search(r'^%s'%re.escape(os.sep),str,re.IGNORECASE):
            str = re.sub(r'^%s'%re.escape(os.sep),"",str)
        return str.lower()
    
    def getKey(self,fileRef):
        m = hashlib.md5()
        m.update(fileRef.getPath().replace('{0}{1}'.format(os.sep,os.sep),os.sep).strip(os.sep).encode('UTF-16'))
        m.update(fileRef.getName().encode('UTF-16'))
        m.update(str(fileRef.getMTime()))
        m.update(str(fileRef.getSize()))
        return m.hexdigest()

    def reduceHash(self,fileIn,hash):
        for fileRef in fileIn.getFileList():
            key = self.getKey(fileRef)
            if key in hash:
                if int(hash[key].getMTime()) != int(fileRef.getMTime()) or hash[key].getSize() != fileRef.getSize():
                    continue
                del hash[key]
        return hash

    def listNewFiles(self,fileRef):
        output = True
        
    def hashDrives(self,toHash,toReduce):
        fileHash = {}
        files = []
        skipList = '|'.join(self._skipList)
        for driveRef in toHash:
            if driveRef.getRootFile():
                fileHash = self.hashify(driveRef.getRootFile(),skipList,fileHash)
        for driveRef in toReduce:
            if driveRef.getRootFile():
                fileHash = self.reduceHash(driveRef.getRootFile(),fileHash)
        files = fileHash.values()
        files = self.sortFiles(files)
        return files
    
    def getDuplicates(self):
        allDuplicates = []
        fileHash = {}
        skipList = '|'.join(self._skipList)
        for driveRef in self._backupDrives:
            if driveRef.getRootFile():
                fileHash, duplicates = self.hashifyDuplicates(driveRef.getRootFile(),skipList,fileHash)
                if duplicates:
                    allDuplicates.extend(duplicates)
        return allDuplicates

    def getNewFiles(self):
        files = self.hashDrives(self._contentDrives,self._backupDrives)
        return files

    def getOldFiles(self):
        files = self.hashDrives(self._backupDrives,self._contentDrives)
        return files

    def getBackupDrive(self,fileRef):
        if self.backupMode == self.BALANCED:
            return self.getMostFreeDrive(self.BACKUP)
        else:
            for driveRef in self._backupDrives:
                if driveRef.findDir(fileRef.getPath()):
                    return driveRef
        return None

    def getContentDrive(self,fileRef):
        for driveRef in self._contentDrives:
            if driveRef.findDir(fileRef.getPath()):
                return driveRef
        return None

    def getMostFreeDrive(self,driveType):
        if not driveType:
            driveType = backupManager.BACKUP
        mostFree = 0
        mostDrive = None
        drives = []
        if driveType == backupManager.BACKUP:
            drives = self._backupDrives
        elif driveType == backupManager.CONTENT:
            drives = self._contentDrives
        for driveRef in drives:
            if driveRef.isBackup():
                if driveRef.isLocked():
                    continue
            freeSpace = driveRef.getFreeSpace()*1024 - driveRef.getPendingWrite()
            if freeSpace > mostFree:
                mostDrive = driveRef
                mostFree = freeSpace
        return mostDrive

    def backupNewFiles(self):
        files = self.getNewFiles()
        toRemove = self.getOldFiles()+self.getDuplicates()
        toStore = {}
        totalRemove = len(toRemove)
        msg = "Sorting files..."
        self.sendMessage(msg)
        logger.info(msg)
        missingDrives = {}
        drivesToRefresh = []
        errorMessages = []
        total = len(files)
        total += totalRemove
        count = 1
        removeCount = 1
        for drive in self.getContentDrives() + self.getBackupDrives():
            drive.clearPendingWrite()
        for removeFile in toRemove:
            buDrive = self.getDrive(removeFile)
            if not buDrive:
                continue
            if buDrive.isLocked():
                error = "%s is locked, cannot remove %s!"%(buDrive.getName(),removeFile.getName())
                errorMessages.append(error)
                logger.error(error)
                continue
            buDrive.addPendingDelete(removeFile)
            id = buDrive.getName() + buDrive.getSerial()
            if id not in toStore:
                toStore[id]={}
                toStore[id]['old']=[]
                toStore[id]['files']=[]
                toStore[id]['drive']=buDrive
            toStore[id]['old'].append(removeFile)
        for fileRef in files:
            buDrive = self.getBackupDrive(fileRef)
            if buDrive:
                if not buDrive.addPendingWrite(fileRef):
                    buDrive = None
            if not buDrive:
                buDrive = self.getMostFreeDrive(backupManager.BACKUP)
            if buDrive:
                if not buDrive.addPendingWrite(fileRef):
                    error = "Not enough space to store "+fileRef.getName()+" on any backup drive, consider adding another backup drive? Unlock backup drives with free space?"
                    errorMessages.append(error)
                    logger.error(error)
                    continue
            if not buDrive:
                error = "Error getting an open drive, no backup drives? All backup drives full? Locked?"
                errorMessages.append(error)
                logger.error(error)
                return errorMessages
            if buDrive.isLocked():
                error = "%s is locked, cannot backup %s!"%(buDrive.getName(),fileRef.getName())
                errorMessages.append(error)
                logger.error(error)
                continue
            id = buDrive.getName()+buDrive.getSerial()
            if id not in toStore:
                toStore[id]={}
                toStore[id]['files']=[]
                toStore[id]['drive']=buDrive
            toStore[id]['files'].append(fileRef)
        for id in toStore.keys():
            buRef = toStore[id]['drive']
            backupLetter = None
            backupLetter = DriveUtil.getLetter(buRef.getName(),buRef.getSerial())
            old = []
            if 'old' in toStore[id]:
                old = toStore[id]['old']
            for fileRef in old:
                msg = "Removing old file " + fileRef.getName() + " on " + buRef.getName() + "... File " + str(count) + "/" + str(total)
                self.sendMessage(msg)
                logger.info(msg)
                if missingDrives.has_key(buRef.getName()+buRef.getSerial()):
                    error = buRef.getName()+"-"+buRef.getSerial()+" not connected, skipping " + fileRef.getName()
                    errorMessages.append(error)
                    logger.error(error)
                    continue
                while not backupLetter:
                    self.sendError(buRef.getName()+"-"+buRef.getSerial())
                    answer = self.getErrorAnswer()
                    if answer:
                        backupLetter = DriveUtil.getLetter(buRef.getName(),buRef.getSerial())
                    else:
                        missingDrives[buRef.getName()+buRef.getSerial()] = buRef
                        backupLetter = False
                        break
                if not backupLetter:
                    error = buRef.getName()+"-"+buRef.getSerial()+" not connected, skipping all files for this drive."
                    errorMessages.append(error)
                    logger.error(error)
                    break
                buName = buRef.getName()
                toRemove = self.fullPath(backupLetter+":\\",buRef.getSetName(),fileRef.getPath(),fileRef.getName())
                try:
                    os.remove(toRemove)
                except:
                    error = "Error deleting " + toRemove
                    errorMessages.append(error)
                    logger.error(error)
                count += 1
                buRef.updateFreeSpace()
            msg = "Backing up new files on " + buRef.getName() + "..."
            self.sendMessage(msg)
            logger.info(msg)
            fileList = []
            if 'files' in toStore[id]:
                fileList = toStore[id]['files']
            for fileRef in fileList:
                msg = "Backing up " + fileRef.getName() + " to " + buRef.getName() + "... File " + str(count) + "/" + str(total)
                self.sendMessage(msg)
                logger.info(msg)
                sourceSerial = fileRef.getSerial()
                sourceName = fileRef.getVolName()
                sourceLetter = DriveUtil.getLetter(sourceName,sourceSerial)
                if not sourceLetter:
                    error = sourceName+"-"+sourceSerial+" missing, skipping " + fileRef.getName()
                    errorMessages.append(error)
                    logger.error(error)
                    continue
                if missingDrives.has_key(buRef.getName()+buRef.getSerial()):
                    error = buRef.getName()+"-"+buRef.getSerial()+" not connected, skipping "+fileRef.getName()
                    errorMessages.append(error)
                    logger.error(error)
                    continue
                while not backupLetter:
                    self.sendError(buDrive.getName()+"-"+buDrive.getSerial())
                    answer = self.getErrorAnswer()
                    if answer == wx.OK:
                        backupLetter = DriveUtil.getLetter(buDrive.getName(),buDrive.getSerial())
                    else:
                        missingDrives[buDrive.getName()+buDrive.getSerial()] = buDrive
                        backupLetter = False
                        break
                if not backupLetter:
                    error = buDrive.getName()+"-"+buDrive.getSerial()+" not connected, skipping all files for this drive."
                    errorMessages.append(error)
                    logger.error(error)
                    break
                try:
                    source = self.fullPath(sourceLetter+":",fileRef.getPath(),fileRef.getName())
                    targetDir = self.createPath(backupLetter+":",buRef.getSetName(),fileRef.getPath())
                except UnicodeDecodeError, e:
                    error = buRef.getName()+"-"+buRef.getSerial()+" can't copy source due to naming issue, skipping: %s" % e
                    errorMessages.append(error)
                    logger.error(error)
                    continue
                if not os.path.exists(self.toFullPath(targetDir)):
                    try:
                        os.makedirs(self.toFullPath(targetDir))
                    except:
                        error = buRef.getName()+"-"+buRef.getSerial()+" can't create dir, skipping "+fileRef.getName()
                        errorMessages.append(error)
                        logger.error(error)
                        continue
                try:
                    shutil.copy2(source,self.fullPath(targetDir,fileRef.getName()))
                except IOError, e:
                    error = buRef.getName()+"-"+buRef.getSerial()+" can't copy source, skipping " + fileRef.getName()+": %s" % e
                    errorMessages.append(error)
                    logger.error(error)
                    continue
                buName = buRef.getName()
                count += 1
                buRef.updateFreeSpace()
            msg = "Refreshing " + buRef.getName() + "-" + buRef.getSerial() + "..."
            self.sendMessage(msg)
            logger.info(msg)
            self.refreshDrive(buRef)
        return errorMessages
        
    def restoreOldFiles(self):
        toRestore = self.getOldFiles()
        toStore = {}
        total = len(toRestore)
        msg = "Sorting files..."
        self.sendMessage(msg)
        logger.info(msg)
        missingDrives = {}
        drivesToRefresh = {}
        errorMessages = []
        count = 1
        for restoreFile in toRestore:
            buDrive = self.getDrive(restoreFile)
            if not buDrive:
                continue
            id = buDrive.getName() + buDrive.getSerial()
            if not toStore.has_key(id):
                toStore[id]={}
                toStore[id]['files']=[]
                toStore[id]['drive']=buDrive
            toStore[id]['files'].append(restoreFile)
        for id in toStore.keys():
            buRef = toStore[id]['drive']
            msg = "Restoring files from " + buRef.getName() + "..."
            self.sendMessage(msg)
            logger.info(msg)
            backupLetter = DriveUtil.getLetter(buRef.getName(),buRef.getSerial())
            for fileRef in toStore[id]['files']:
                mostFree = False
                contentRef = self.getContentDrive(fileRef)
                if not contentRef:
                    mostFree = True
                    contentRef = self.getMostFreeDrive(backupManager.CONTENT)
                if not contentRef.addPendingWrite(fileRef):
                    if not mostFree:
                        contentRef = self.getMostFreeDrive(backupManager.CONTENT)
                    if not contentRef.addPendingWrite(fileRef):
                        error = "Cannot find a content drive with enough space to store " + fileRef.getName() + ", all drives full?"
                        errorMessages.append(error)
                        logger.error(error)
                        continue
                contentLetter = DriveUtil.getLetter(contentRef.getName(),contentRef.getSerial())
                if not contentLetter:
                    error = contentRef.getName()+"-"+contentRef.getSerial()+" not connected, skipping "+fileRef.getName()
                    errorMessages.append(error)
                    logger.error(error)
                msg = "Restoring "+fileRef.getName()+" to "+contentRef.getName()+"... File "+str(count)+"/"+str(total)
                self.sendMessage(msg)
                logger.info(msg)
                if missingDrives.has_key(buRef.getName()+buRef.getSerial()):
                    errorMessages.append(buRef.getName()+"-"+buRef.getSerial())
                while backupLetter is None:
                    self.sendError(drive.getName()+"-"+drive.getSerial())
                    answer = self.getErrorAnswer()
                    if answer:
                        backupLetter = DriveUtil.getLetter(buRef.getName(),buRef.getSerial())
                    else:
                        missingDrives[buRef.getName()+buRef.getSerial()] = buRef
                        backupLetter = False
                        break
                if not backupLetter:
                    error = buRef.getName()+"-"+buRef.getSerial()+" not connected, skpping restore for all missing files from this drive."
                    errorMessages.append(error)
                    logger.error(error)
                    break
                buName = buRef.getName()
                source = self.fullPath(backupLetter+":",buRef.getSetName(),fileRef.getPath(),fileRef.getName())
                targetDir = self.createPath(contentLetter+":",fileRef.getPath())
                if not os.path.exists(self.toFullPath(targetDir)):
                    try:
                        os.makedirs(self.toFullPath(targetDir))
                    except:
                        error = contentRef.getName()+"-"+contentRef.getSerial()+" can't create dir, skpping " + fileRef.getName()
                        errorMessages.append(error)
                        logger.error(error)
                        continue
                try:
                    shutil.copy2(source,self.fullPath(targetDir,fileRef.getName()))
                except:
                    error = contentRef.getName()+"-"+contentRef.getSerial()+" can't copy source, skpping "+fileRef.getName()
                    logger.error(error)
                    errorMessages.append(error)
                    continue
                count += 1
                contentRef.updateFreeSpace()
                drivesToRefresh[contentRef.getName()+contentRef.getSerial()]=contentRef
        for drive in drivesToRefresh.keys():
            msg = "Refreshing "+drivesToRefresh[drive].getName()+"-"+drivesToRefresh[drive].getSerial()+"..."
            self.sendMessage(msg)
            logger.info(msg)
            self.refreshDrive(drivesToRefresh[drive])
        return errorMessages

    def refresh(self,drives,remote=False):
        errorMessages = []
        for drive in drives:
            proceed = False
            if not drive.isBackup():
                if remote and drive.remoteReady():
                    msg = 'Remote indexing {0}-{1}'.format(drive.getName(),drive.getSerial())
                    logger.info(msg)
                    self.sendMessage(msg)
                    result = self.remoteIndexDrive(drive)
                    if not result:
                        error = 'Error remote indexing {0}-{1}, unknown error'.format(drive.getName(),drive.getSerial())
                        logger.error(error)
                        self.sendMessage(error)
                        errorMessages.append(error)
                    elif result == drive.NOTEXIST:
                        error = 'Remote path for {0}-{1} does not exist'.format(drive.getName(),drive.getSerial())
                        logger.error(error)
                        self.sendMessage(error)
                        errorMessages.append(error)
                    else:
                        msg = 'Remote indexing done for {0}-{1}'.format(drive.getName(),drive.getSerial())
                        logger.info(msg)
                        self.sendMessage(msg)
                else:
                    proceed = True
            else:
                proceed = True
            if not remote or proceed:
                msg = 'Refreshing {0}-{1}'.format(drive.getName(),drive.getSerial())
                logger.info(msg)
                self.sendMessage(msg)
                result = self.refreshDrive(drive)
                while result == self.MISSING:
                    self.sendError(drive.getName()+"-"+drive.getSerial())
                    answer = self.getErrorAnswer()
                    if answer:
                        result = self.refreshDrive(drive)
                    else:
                        error = 'Could not refresh {0}-{1}, drive is not connected'.format(drive.getName(),drive.getSerial())
                        logger.error(error)
                        self.sendMessage(error)
                        errorMessages.append(error)
                        result = 13
                        break
                if result == 13:
                    continue
                if not result:
                    error = 'Could not refresh {0}-{1}, unknown error'.format(drive.getName(),drive.getSerial())
                    logger.error(error)
                    self.sendMessage(error)
                else:
                    msg = "Refreshed " + drive.getName() + "-" + drive.getSerial() + "!"
                    logger.info(msg)
                    self.sendMessage(msg)
        return errorMessages